package org.yaukie.auth.service.impl;

import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.yaukie.auth.entity.LoginUser;
import org.yaukie.auth.service.api.TokenHandlerService;
import org.yaukie.base.redis.RedisOrMapCache;
import org.yaukie.base.util.IpUtil;
import org.yaukie.base.util.SpringContextUtil;
import org.yaukie.base.util.StringTools;
import org.yaukie.base.uuid.IdUtils;

@Component
public class TokenHandlerServiceImpl implements TokenHandlerService {
    private static final Logger log = LoggerFactory.getLogger(TokenHandlerServiceImpl.class);
    @Autowired
    private RedisOrMapCache redisCache;
    @Value("${yaukie.token.header:Authorization}")
    private String tokenHeader;
    @Value("${yaukie.token.expire:10}")
    private String tokenExpired;
    @Value("${yaukie.token.secret:}")
    private String secret;
    @Value("${yaukie.token.refresh:1}")
    private String refreshExpired;

    public TokenHandlerServiceImpl() {
    }

    public String buildToken(LoginUser loginUser) {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        long current = System.currentTimeMillis();
        long expried = current + Long.parseLong(this.tokenExpired) * 60L * 1000L;
        loginUser.setExpireTime(expried);
        loginUser.setLoginTime(new Date());
        this.setUserAgent(loginUser);
        this.refreshToken(loginUser);
        Map dataMap = new HashMap();
        dataMap.put("login_user_key", token);
        return Jwts.builder().setClaims(dataMap).signWith(SignatureAlgorithm.HS512, "yuenbinisagreatman!").compact();
    }

    public boolean verifyToken(HttpServletRequest request) {
        if (log.isInfoEnabled()) {
            log.info("令牌验证开始....");
        }

        String token = this.getToken(request);
        if (StringTools.isEmpty(token)) {
            return false;
        } else {
            LoginUser loginUser = (LoginUser)this.redisCache.getCacheObject(this.getTokenKey(token));
            if (StringTools.isNull(loginUser)) {
                return false;
            } else {
                long expired = loginUser.getExpireTime();
                long current = System.currentTimeMillis();
                if (current > expired) {
                    log.info("用户会话标识[token]已经过期...");
                    return false;
                } else {
                    if (expired - current <= Long.parseLong(this.refreshExpired)) {
                        this.refreshToken(loginUser);
                        log.info("登录令牌{}刷新成功", token);
                    }

                    if (log.isInfoEnabled()) {
                        log.info("令牌验证结束....");
                    }

                    return true;
                }
            }
        }
    }

    public void refreshToken(LoginUser loginUser) {
        String token = loginUser.getToken();
        loginUser.setExpireTime(System.currentTimeMillis() + (long)(Integer.parseInt(this.tokenExpired) * 60 * 1000));
        String tokenKey = this.getTokenKey(token);
        this.redisCache.setCacheObject(tokenKey, loginUser, Integer.parseInt(this.tokenExpired), TimeUnit.MINUTES);
    }

    public void delLoginUser(String token) {
        if (StringTools.isNotEmpty(token)) {
            String tokenKey = this.getTokenKey(token);
            this.redisCache.deleteObject(tokenKey);
        }

    }

    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(this.tokenHeader);
        if (StringTools.isEmpty(token)) {
            token = request.getHeader("token");
        } else if (token.contains("Bearer ") && (token.startsWith("Bearer ") || token.indexOf("Bearer ") != -1)) {
            token = token.replace("Bearer ", "");
            Claims claims = this.parseToken(token);
            token = claims.get("login_user_key") + "";
        }

        if (StringTools.isEmpty(token)) {
            token = "";
        }

        return token;
    }

    public Claims parseToken(String jwtToken) {
        Claims claims = (Claims)Jwts.parser().setSigningKey("yuenbinisagreatman!").parseClaimsJws(jwtToken).getBody();
        return claims;
    }

    public LoginUser getLoginUser(HttpServletRequest request) {
        LoginUser loginUser = null;
        String token = this.getToken(request);
        if (!this.isTokenExpired(token)) {
            loginUser = (LoginUser)this.redisCache.getCacheObject(this.getTokenKey(token));
        }

        return loginUser;
    }

    public String getTokenKey(String uid) {
        return "login_token_" + uid;
    }

    public Authentication getAuthenticationFromToken(HttpServletRequest request) {
        Authentication authentication = null;
        String token = this.getToken(request);
        if (StringTools.isEmpty(token)) {
            return null;
        } else {
            if (!this.isTokenExpired(token)) {
                authentication = SecurityContextHolder.getContext().getAuthentication();
            }

            return authentication;
        }
    }

    public String getUserNameFromAuthentication(HttpServletRequest request) {
        Authentication authentication = this.getAuthenticationFromToken(request);
        String userName = null;
        if (null != authentication) {
            Object principal = authentication.getPrincipal();
            if (principal != null && principal instanceof UserDetails) {
                userName = ((UserDetails)principal).getUsername();
            }
        }

        return userName;
    }

    public boolean isTokenExpired(String token) {
        LoginUser loginUser = (LoginUser)this.redisCache.getCacheObject(this.getTokenKey(token));
        if (StringTools.isNull(loginUser)) {
            return true;
        } else {
            long expired = loginUser.getExpireTime();
            long current = System.currentTimeMillis();
            return current > expired;
        }
    }

    private void setUserAgent(LoginUser loginUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(SpringContextUtil.getRequest().getHeader("User-Agent"));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
        loginUser.setIpaddr(IpUtil.getIpAddr(SpringContextUtil.getRequest()));
        loginUser.setLoginLocation(IpUtil.getRealAddressByIP(IpUtil.getIpAddr(SpringContextUtil.getRequest())));
    }
}
